package com.lahuan.config.datasouce;

import com.lahuan.config.datasouce.entity.GlobalXid;
import com.lahuan.config.datasouce.entity.TransactionBranch;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import javax.transaction.xa.XAException;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.UUID;

/**
 * @author lfy
 */
@Aspect
@Component
public class AopTransactionManager {


    /**
     * 环绕增强
     * @param pjd
     * @param transaction
     * @return
     * @throws Throwable
     */
    @Around("@annotation(transaction)")
    public Object around(ProceedingJoinPoint pjd, CustomTransaction transaction) throws Throwable {
        //是否是开启全局事务的方法
        boolean isStartTransactionMethod = false;
        boolean isRunConfirm = false;
        boolean isRunCancel = false;
        try {
            Object[] args = pjd.getArgs();
            Signature s = pjd.getSignature();
            String methodName = s.getName();

            GlobalXid globalXid = CustomTransactionHolder.getGlobalXid();
            TransactionBranch branch = initTransactionBranch( pjd,transaction, args);
            if(globalXid==null){
                isStartTransactionMethod = true;
                globalXid = genGlobalXid();
                CustomTransactionHolder.setGlobalXid(globalXid);

            }
            String branchXid = genBranchXid(methodName, globalXid);
            branch.setBranchXid(branchXid);
            branch.setGlobalXid(globalXid);
            CustomTransactionHolder.addTransactionBranchSet(branch);
            if(isStartTransactionMethod){
                System.out.println(methodName+"开启一个全局事务,branchXid:"+branchXid);
            }else{
                System.out.println(methodName+"加入一个全局事务,branchXid:"+branchXid);
            }
            Object result = pjd.proceed(args);
            if(isStartTransactionMethod){
                //开启全局事务的方法的代理才进入这里
                //检查是否出现异常
                System.out.println("try阶段事务结束,开始检测是否异常");
                if(CustomTransactionHolder.getTransactionException()){
                    //出现过异常就回滚
                    System.out.println("try阶段事务结束,执行过程中发现异常,执行cancel");
                    isRunCancel = true;
                    transactionCancel();
                }else{
                    //未出现异常就confirm
                    System.out.println("try阶段事务结束,未发现异常,执行confirm");
                    isRunConfirm= true;
                    confirm();
                }
            }
            return result;
        } catch (Throwable throwable) {
            //出现异常直接回滚
            CustomTransactionHolder.setTransactionException();
            if(isStartTransactionMethod ){
                if(isRunConfirm){
                    System.out.println("try阶段事务结束,confirm阶段出现异常,执行cancel,globalXid"+CustomTransactionHolder.getGlobalXid());
                    transactionCancel();
                }else if(isRunCancel){
                    System.out.println("try阶段事务结束,执行cancel出现异常,globalXid"+CustomTransactionHolder.getGlobalXid());
                    //transactionCancel();
                }else{
                    System.out.println("try阶段事务未结束,出现异常,执行cancel,globalXid:"+CustomTransactionHolder.getGlobalXid());
                    transactionCancel();
                }
            }
            throw throwable;
        }finally {
            try {
                if(isStartTransactionMethod ){
                    System.out.println("清空事务上下文");
                    CustomTransactionHolder.clear();
                }
            }catch (Exception ex) {
                System.out.println("清空事务上下文出现异常");
                ex.printStackTrace();
            }
        }

    }

    /**
     * 把所有的事务都confirm
     */
    private void confirm() throws Throwable {
        HashSet<TransactionBranch> set = CustomTransactionHolder.getTransanctionBranchSet();
        if(set ==null || set.size()==0){
            return ;
        }
        for (TransactionBranch branch : set) {
            String methodName = branch.getConfirmMethod();
            invokeMethod(branch, methodName);
        }
        System.out.println("confirm全部执行完成,事务成功");
    }




    /**
     * 把所有的事务都回滚
     */
    private void transactionCancel() throws Throwable {
        HashSet<TransactionBranch> set = CustomTransactionHolder.getTransanctionBranchSet();
        if(set ==null || set.size()==0){
            return ;
        }
        for (TransactionBranch branch : set) {
            String methodName = branch.getCancelMethod();
            invokeMethod(branch, methodName);
        }
        System.out.println("cancel全部执行完成");
    }

    /**
     * 调用方法
     * @param branch
     * @param methodName
     * @throws Throwable
     */
    private void invokeMethod(TransactionBranch branch, String methodName) throws Throwable {
        Object[] args = branch.getArgs();
        Object target = branch.getTarget();
        Class<?>[] parameterTypes = branch.getParameterTypes();
        //Class<?> returnType = branch.getReturnType();
        Class<?> targetClass = target.getClass();

        Method method = targetClass.getMethod(methodName, parameterTypes);
        method.invoke(target,args);

//        MethodHandles.Lookup lookup = MethodHandles.lookup();
//        MethodType methodType = MethodType.methodType(void.class, parameterTypes);
//        MethodHandle methodHandle = lookup.findVirtual(targetClass, methodName, methodType);
//        methodHandle.invokeExact(target,args);
    }
    /**
     * 初始化分支事务信息
     *
     * @param pjd
     * @param transaction
     * @param args
     * @return
     */
    private TransactionBranch initTransactionBranch(ProceedingJoinPoint pjd, CustomTransaction transaction, Object[] args) {
        String confirmMethod = transaction.confirmMethod();
        String cancelMethod = transaction.cancelMethod();
        TransactionBranch branch = new TransactionBranch();
        branch.setConfirmMethod(confirmMethod);
        branch.setCancelMethod(cancelMethod);
        branch.setArgs(args);
        branch.setTarget(pjd.getTarget());
        MethodSignature methodSignature = (MethodSignature) pjd.getSignature();
        Class<?>[] parameterTypes = methodSignature.getParameterTypes();
        Class<?> returnType = methodSignature.getReturnType();
        branch.setParameterTypes(parameterTypes);
        branch.setReturnType(returnType);
        return branch;
    }
    /**
     * 生成分支事务id
     * @param signature
     * @param globalXid
     * @return
     */
    private String genBranchXid(String signature, GlobalXid globalXid) {
        return signature+"-"+globalXid.getXid() +"-"+ UUID.randomUUID().toString();
    }


    /**
     * 生成全局事务id
     * @return
     */
    private GlobalXid genGlobalXid() {
        //xid生成，这里用UUID来简化，实际环境需要保证唯一
        GlobalXid globalXid = new GlobalXid();
        globalXid.setXid(UUID.randomUUID().toString());
        return globalXid;
    }




}
